/*
 * main.c
 * 
 * Copyright(c) 2021 Cai_XL <Cai_XL@outlook.com>
 * bilibili : https://space.bilibili.com/54910927
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "main.h"

#define USE_FIFO

static void CCU_Init(void);
static void UART_Init(void);
static void UART_SendByte(uint8_t);
static void UART_SendString(uint8_t*);

int main(void)
{
    CCU_Init();     // initialize CCU
    UART_Init();    // initialize UART

    GPIOE->Pn_CFG0 &= ~(0x0f << (4*2));
    GPIOE->Pn_CFG0 |= 0x01 << (4*2);

    while(1)
    {
        // send string
        UART_SendString((uint8_t*)"Hello from F1C100S\n");

        // simple delay
        {
            volatile uint32_t i = 50000;
            while(i--);
        }
    }
}

// CPUCLK:408MHz
// AHBCLK:200MHz
// APBCLK:100MHz
static void CCU_Init(void)
{
    uint32_t timeout;						// variable to wait PLL stable

	CCU->AHB_APB_HCLKC_CFG_REG &= ~(3<<8);	// APB_CLK_DIV_RATIO = 2
	CCU->AHB_APB_HCLKC_CFG_REG &= ~(3<<4);	// AHB_CLK_DIV_RATIO = 1
	CCU->AHB_APB_HCLKC_CFG_REG |= (2<<6);	//AHB_PRE_DIV = 3

	/* enable PLL_PERIPH */
	CCU->PLL_GLOBAL_TIME_REG = 0x1ff;		// PLL lock time
	CCU->PLL_PERIPH_CTRL_REG |= (1<<31);	// enable PLL_PERIPH in 600MHz(default)
	timeout = 0xfff;
	while((timeout--) && !(CCU->PLL_PERIPH_CTRL_REG & (1<<28)));	// wait PLL_PERIPH stable

	CCU->AHB_APB_HCLKC_CFG_REG |= (3<<12);	// AHB_CLK_SRC = PLL_PERIPH / AHB_PRE_DIV

	CCU->PLL_CPU_TIME_REG = 0x1ff;			// PLL lock time
	CCU->PLL_CPU_CTRL_REG |= (1<<31);		// enable PLL_CPU in 408MHz(default)
	timeout = 0xfff;
	while((timeout--) && !(CCU->PLL_CPU_CTRL_REG & (1<<28)));	// wait PLL_CPU stable

	CCU->CPU_CLK_SRC_REG |= (1<<17);		// CPUCLK = PLL_CPU

    timeout = 0xfff;
    while(timeout--);                       // wait clock stble
}

static void UART_Init(void)
{
    // GPIO config
    GPIOE->Pn_CFG0 &= ~0xff;    // ret multiplexing functionses
    GPIOE->Pn_CFG0 |= 0x55;     // remap to UART0
    GPIOE->Pn_PUL0 &= ~0x0f;    // reset pull-up & pull-down
    GPIOE->Pn_PUL0 |= 0x05;     // pull-up

    //clock config
    CCU->APB1_CLK_GATING_REG |= (1<<20);    // enable UART0 clock
    NOP();                                  // wait clock stable
    NOP();
    NOP();
    NOP();
    CCU->APB1_SOFT_RST_REG |= (1<<20);      // de-assert UART0 reset

    // 115200bps, 8bit data, 1stopbit, no parity
    UART0->Group2.UART_IER_REG = 0x00;  // disable all interrupt
    UART0->UART_MCR_REG = 0x00;         // reset Modem

    #if defined(USE_FIFO)
        UART0->Group3.UART_FCR_REG |= (1<<2);
        // UART_FCR_REG[2] : XFIFOR
        // 0 : no action
        // 1 : reset the index of the Tx FIFO,treats it as empty

        UART0->Group3.UART_FCR_REG |= (1<<0);
        // UART_FCR_REG[0] : FIFOE
        // 0 : disable FIFOs(= Tx FIFO + Rx FIFO)
        // 1 : enable FIFOs

    #endif

    UART0->UART_LCR_REG |= (1<<7);      // set DLAB to 1
    UART0->Group1.UART_DLL_REG = (100000000/(115200*16)) & 0xff;
    UART0->Group2.UART_DLH_REG = (100000000/(115200*16)) >> 8;
    UART0->UART_LCR_REG &= ~(1<<7);     // set DLAB to 0

    UART0->UART_LCR_REG &= ~0x2f;       // reset parity,stopbit,databit
    UART0->UART_LCR_REG |= 0x03;        // 8bit of data, no parity, 1 stopbit 

}

static void UART_SendByte(uint8_t data)
{
    // wait FIFO avaliable
    #if defined(USE_FIFO)
        while(!(UART0->UART_USR_REG & (1<<1)));
        // UART_USR_REG[1] : TFNF (=Tx FIFO Not Full)
        // 0 : Tx FIFO is full
        // 1 : Tx FIFO is not full
    #else
    // wait THR avaliable
        while(!(UART0->UART_LSR_REG & (1<<5)));
    #endif

    UART0->Group1.UART_THR_REG = data;
}

static void UART_SendString(uint8_t* str)
{
    GPIOE->Pn_DATA |= 1<<2;
    NOP();
    NOP();
    NOP();
    NOP();
    while(*str)
    {
        UART_SendByte(*str);    // byte by byte
        str++;

        GPIOE->Pn_DATA ^= 1<<2;
    }
}